#pragma once
#include "logger.hpp"
#include <brpc/channel.h>
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>

namespace ken_im
{
// 1. 封装单个服务的信道管理类:
/**
 * @class ServiceChannel
 * @brief 服务信道管理类，用于管理服务节点及其对应的通信信道。
 *
 * 该类提供了一种机制，用于动态管理服务节点及其通信信道。通过该类，可以方便地添加、删除和选择服务节点，以实现负载均衡和故障转移。
 *
 * 核心功能：
 * - 添加服务节点信道（append）
 * - 删除服务节点信道（remove）
 * - 选择一个信道用于发起RPC调用（choose）
 *
 * 使用示例：
 * @code
 * ServiceChannel service("example_service");
 * service.append("192.168.1.1:12345");
 * service.append("192.168.1.2:12345");
 * auto channel = service.choose();
 * if (channel) {
 *     // 使用选中的信道发起RPC调用
 * }
 * @endcode
 *
 * 构造函数参数：
 * - @param name 服务名称，用于标识该服务信道管理实例。
 *
 * 注意事项：
 * - 在添加或删除信道时，需要确保操作线程安全。
 * - 选择信道时，使用的是简单的轮询策略。
 */
class ServiceChannel
{
  public:
    using ptr = std::shared_ptr<ServiceChannel>;
    using ChannelPtr = std::shared_ptr<brpc::Channel>;
    ServiceChannel( const std::string &name )
        : _service_name( name ), _index( 0 )
    {
    }
    // 服务上线了一个节点，则调用append新增信道
    void append( const std::string &host )
    {
        auto channel = std::make_shared<brpc::Channel>();
        brpc::ChannelOptions options;
        options.connect_timeout_ms = -1;
        options.timeout_ms = -1;
        options.max_retry = 3;
        options.protocol = "baidu_std";
        int ret = channel->Init( host.c_str(), &options );
        if ( ret == -1 )
        {
            LOG_ERROR( "初始化{}-{}信道失败!", _service_name, host );
            return;
        }
        std::unique_lock<std::mutex> lock( _mutex );
        _hosts.insert( std::make_pair( host, channel ) );
        _channels.push_back( channel );
    }
    // 服务下线了一个节点，则调用remove释放信道
    void remove( const std::string &host )
    {
        std::unique_lock<std::mutex> lock( _mutex );
        auto it = _hosts.find( host );
        if ( it == _hosts.end() )
        {
            LOG_WARN( "{}-{}节点删除信道时，没有找到信道信息！", _service_name,
                      host );
            return;
        }
        for ( auto vit = _channels.begin(); vit != _channels.end(); ++vit )
        {
            if ( *vit == it->second )
            {
                _channels.erase( vit );
                break;
            }
        }
        _hosts.erase( it );
    }
    // 通过RR轮转策略，获取一个Channel用于发起对应服务的Rpc调用
    ChannelPtr choose()
    {
        std::unique_lock<std::mutex> lock( _mutex );
        if ( _channels.size() == 0 )
        {
            LOG_ERROR( "当前没有能够提供 {} 服务的节点！", _service_name );
            return ChannelPtr();
        }
        int32_t idx = _index++ % _channels.size();
        return _channels[idx];
    }

  private:
    std::mutex _mutex;
    int32_t _index;                    // 当前轮转下标计数器
    std::string _service_name;         // 服务名称
    std::vector<ChannelPtr> _channels; // 当前服务对应的信道集合
    std::unordered_map<std::string, ChannelPtr>
        _hosts; // 主机地址与信道映射关系
};

// 总体的服务信道管理类
/**
 * @class ServiceManager
 * @brief 服务管理器类，用于管理服务的上线和下线，并选择指定服务的节点信道。
 *
 * 该类提供了以下核心功能：
 * - 选择指定服务的节点信道
 * - 声明关注的服务的上下线
 * - 服务上线时的回调接口
 * - 服务下线时的回调接口
 *
 * 使用示例：
 * @code
 * ServiceManager::ptr manager = std::make_shared<ServiceManager>();
 * manager->declared("example_service");
 * ServiceChannel::ChannelPtr channel = manager->choose("example_service");
 * @endcode
 *
 * 构造函数参数：
 * - 无参数
 *
 * 使用限制：
 * - 需要确保在多线程环境下正确使用互斥锁
 * - 服务名称和实例名称的格式需要符合特定规则
 *
 * 潜在副作用：
 * - 如果服务名称或实例名称不符合预期格式，可能会导致异常或错误日志
 */
class ServiceManager
{
  public:
    using ptr = std::shared_ptr<ServiceManager>;
    ServiceManager()
    {
    }
    // 获取指定服务的节点信道
    ServiceChannel::ChannelPtr choose( const std::string &service_name )
    {
        std::unique_lock<std::mutex> lock( _mutex );
        auto sit = _services.find( service_name );
        if ( sit == _services.end() )
        {
            LOG_ERROR( "当前没有能够提供 {} 服务的节点！", service_name );
            return ServiceChannel::ChannelPtr();
        }
        return sit->second->choose();
    }
    // 先声明，我关注哪些服务的上下线，不关心的就不需要管理了
    void declared( const std::string &service_name )
    {
        std::unique_lock<std::mutex> lock( _mutex );
        _follow_services.insert( service_name );
    }
    // 服务上线时调用的回调接口，将服务节点管理起来
    void onServiceOnline( const std::string &service_instance,
                          const std::string &host )
    {
        std::string service_name = getServiceName( service_instance );
        ServiceChannel::ptr service;
        {
            std::unique_lock<std::mutex> lock( _mutex );
            auto fit = _follow_services.find( service_name );
            if ( fit == _follow_services.end() )
            {
                LOG_DEBUG( "{}-{} 服务上线了，但是当前并不关心！", service_name,
                           host );
                return;
            }
            // 先获取管理对象，没有则创建，有则添加节点
            auto sit = _services.find( service_name );
            if ( sit == _services.end() )
            {
                service = std::make_shared<ServiceChannel>( service_name );
                _services.insert( std::make_pair( service_name, service ) );
            }
            else
            {
                service = sit->second;
            }
        }
        if ( !service )
        {
            LOG_ERROR( "新增 {} 服务管理节点失败！", service_name );
            return;
        }
        service->append( host );
        LOG_DEBUG( "{}-{} 服务上线新节点，进行添加管理！", service_name, host );
    }
    // 服务下线时调用的回调接口，从服务信道管理中，删除指定节点信道
    void onServiceOffline( const std::string &service_instance,
                           const std::string &host )
    {
        std::string service_name = getServiceName( service_instance );
        ServiceChannel::ptr service;
        {
            std::unique_lock<std::mutex> lock( _mutex );
            auto fit = _follow_services.find( service_name );
            if ( fit == _follow_services.end() )
            {
                LOG_DEBUG( "{}-{} 服务下线了，但是当前并不关心！", service_name,
                           host );
                return;
            }
            // 先获取管理对象，没有则创建，有则添加节点
            auto sit = _services.find( service_name );
            if ( sit == _services.end() )
            {
                LOG_WARN( "删除{}服务节点时，没有找到管理对象", service_name );
                return;
            }
            service = sit->second;
        }
        service->remove( host );
        LOG_DEBUG( "{}-{} 服务下线节点，进行删除管理！", service_name, host );
    }

  private:
    std::string getServiceName( const std::string &service_instance )
    {
        auto pos = service_instance.find_last_of( '/' );
        if ( pos == std::string::npos )
            return service_instance;
        return service_instance.substr( 0, pos );
    }

  private:
    std::mutex _mutex;
    std::unordered_set<std::string> _follow_services;
    std::unordered_map<std::string, ServiceChannel::ptr> _services;
};
} // namespace ken_im

/**
 *      A[启动程序] --> B[解析命令行参数]
        B --> C[初始化日志系统]
        C --> D[创建ServiceManager]
        D --> E[声明服务路径]
        D --> F[绑定服务上下线回调]
        F --> G[创建Discovery对象]
        G --> H[监听ETCD目录变化]
        H --> I[触发回调更新服务状态]
 */